package ttt;

import java.util.Random;
class TicTacToe
{
	public static final int HUMAN        = 0; 
	public static final int COMPUTER     = 1; 
	public  static final int EMPTY        = 2;

	public  static final int HUMAN_WIN    = 0;
	public  static final int DRAW         = 1;
	public  static final int UNCLEAR      = 2;
	public  static final int COMPUTER_WIN = 3;

	private int [ ] [ ] board = new int[ 3 ][ 3 ];
    private Random random=new Random();  
	private int side=random.nextInt(2);  
	private int position=UNCLEAR;
	private char computerChar,humanChar;
	
	int rowArray[] = {0,0,0,1,1,1,2,2,2};
	int columnArray[] = {0,1,2,0,1,2,0,1,2};

	// Constructor
	public TicTacToe( )
	{
		clearBoard( );
		initSide();
	}
	
	private void initSide()
	{
	    if (this.side==COMPUTER) { computerChar='X'; humanChar='O'; }
		else                     { computerChar='O'; humanChar='X'; }
    }
    
    public void setComputerPlays()
    {
        this.side=COMPUTER;
        initSide();
    }
    
    public void setHumanPlays()
    {
        this.side=HUMAN;
        initSide();
    }

	public boolean computerPlays()
	{
	    return side==COMPUTER;
	}

	public int chooseMove()
	{
	    Best best=chooseMove(COMPUTER, 1, -1);
	    return best.row*3+best.column;
	    //return 0;
    }
    
    // Find optimal move
	private Best chooseMove( int side, int alpha, int beta)
	{
		int opp;              // The other side
		Best reply;           // Opponent's best reply
		int simpleEval;       // Result of an immediate evaluation
		int bestRow = 0;
		int bestColumn = 0;
		int value;
		//System.out.println(side);
		if( ( simpleEval = positionValue( ) ) != UNCLEAR )
			return new Best( simpleEval );
		//prevents from making two loops.
		if(side == HUMAN){
			opp = COMPUTER;
			value = alpha;
		}else{
			opp = HUMAN;
			value = beta;
		}
		for(int i = 0; i < 9; i++){
			int row = rowArray[i];
			int column = columnArray[i];
			
			if(squareIsEmpty(row,column)){
				//places sign into board.
				place(row, column, side);
				reply = chooseMove(opp, alpha, beta);
				//removes sign from board, else you will get the whole board back instead of just 1 move
				place(row, column, EMPTY);
				
				if(side == COMPUTER){
					//Computer is the maximizer
					//if reply.val is bigger than alpha then reply.val == COMPUTER_WINS
					if(reply.val > alpha){
						alpha = reply.val;
						value = reply.val;
						
					}
					if(alpha >= beta){
						//System.out.println("side: " + side + " row: " + row + " column: " + column);
							bestRow = row;
							bestColumn = column;
							
							break;
					}
				}else{
					if(reply.val < beta){
						beta = reply.val;
						value = reply.val;
					}
					if(alpha >= beta){
						//System.out.println("side: " + side + " row: " + row + " column: " + column);
						bestRow = row;
						bestColumn = column;
						break;
					}
				}
			}
			
		}
		// TODO: implementeren m.b.v. recursie/backtracking
	    return new Best(value, bestRow, bestColumn);
    }

   
    //check if move ok
    public boolean moveOk(int move)
    {
    	return ( move>=0 && move <=8 && board[move/3 ][ move%3 ] == EMPTY );
    }
    
    // play move
    public void playMove(int move)
    {
		board[move/3][ move%3] = this.side;
		if (side==COMPUTER) this.side=HUMAN;  else this.side=COMPUTER;
	}


	// Simple supporting routines
	private void clearBoard( )
	{
		for(int i = 0; i < 3; i++){
			for(int j = 0; j < 3; j++){
				board[i][j] = EMPTY;
			}
		}
	}


	public boolean boardIsFull( )
	{
		for(int i = 0; i < 3; i++){
			for(int j = 0; j < 3; j++){
				if(board[i][j] == EMPTY){
					return false;
				}
			}
		}
		return true;
	}

	// Returns whether 'side' has won in this position
	public boolean isAWin( int side )
	{
		int countColumns = 0;
		int row;
		int column;
		//check for columns
		for(int i = 0; i < 3; i++){
	    	countColumns = 0;
			for(int j = 0; j < 3; j++){
		    	if(board[i][j] == side){
		    		countColumns++;
		    	}
		    	
		    	if(i == 0 && j < 3){
			    	if(board[i][j] == side && board[i + 1][j] == side && board[i + 2][j] == side){
			    		return true;
			    	}
		    	}
		    	
			}
	    	if(countColumns == 3){
	    		return true;
	    	}
		}
	    //check for diagonal
	    if(board[0][0] == side && board[1][1] == side && board[2][2] == side){
	    	return true;
	    }
	    if(board[0][2] == side && board[1][1] == side && board[2][0] == side){
	    	return true;
	    }

	    return false;
    }

	// Play a move, possibly clearing a square
	private void place( int row, int column, int piece )
	{
		board[ row ][ column ] = piece;
	}

	private boolean squareIsEmpty( int row, int column )
	{
		return board[ row ][ column ] == EMPTY;
	}

	// Compute static value of current position (win, draw, etc.)
	public int positionValue( )
	{
		if(isAWin(COMPUTER)){
			return COMPUTER_WIN;
		}
		if(isAWin(HUMAN)){
			return HUMAN_WIN;
		}
		if(boardIsFull()){
			return DRAW;
		}
		return UNCLEAR;
	}
	
	
	public String toString()
	{
		String boardString = "";
		
		for(int i = 0; i < 3; i++){
			for(int j = 0; j < 3; j++){
				if(board[i][j] == COMPUTER){
					boardString += computerChar;
				}
				if(board[i][j] == HUMAN){
					boardString += humanChar;
				}
				if(board[i][j] == EMPTY){
					boardString += "-";
				}
			}
			boardString += "\n";
		}
		return boardString;
		/*
        String out = "";
        out = "";
        for (int row = 0; row < board.length; row++) {
                for (int column = 0; column < board[1].length; column++) {
                        if (board[row][column] == COMPUTER) {
                                out += computerChar;
                        } else if (board[row][column] == HUMAN) {
                                out += humanChar;
                        } else {
                                out += "_";
                        }
                }
                out += "\n";
        }

        return out;*/
	}  
	
	public boolean gameOver()
	{
	    this.position=positionValue();
	    return this.position!=UNCLEAR;
    }
    
    public String winner()
    {
        if      (this.position==COMPUTER_WIN) return "computer";
        else if (this.position==HUMAN_WIN   ) return "human";
        else                                  return "nobody";
    }
    
	
	private class Best
    {
       int row;
       int column;
       int val;

       public Best( int v )
         { this( v, 0, 0 ); }
      
       public Best( int v, int r, int c )
        { val = v; row = r; column = c; }
    } 
	
	
}

